// Copyright (c) Microsoft Corporation.  All rights reserved.
#include "StdAfx.h"
#include "DPWSServiceWrapper.h"

#include "SideShowServiceType.h"
#include "SideShowServiceTypeTypes.h"


#include "SideShowService.h"

#include "EventSourceNotificationUtility.h"

#include <strsafe.h>

// Constant variables definition start
// Device metadata.
WSD_LOCALIZED_STRING thisDeviceName = {NULL, _T("Microsoft SideShow Device Simulator")};
WSD_LOCALIZED_STRING_LIST thisDeviceNameList = {NULL, &thisDeviceName};
const WSD_THIS_DEVICE_METADATA thisDeviceMetadata = {
    &thisDeviceNameList,    // FriendlyName
    L"0.165",               // FirmwareVersion
    L"6123456789",          // SerialNumber
};

//------------------------------------------------------------------------------
// CDPWSServiceWrapper::CDPWSServiceWrapper
//      Constructor
//------------------------------------------------------------------------------
CDPWSServiceWrapper::CDPWSServiceWrapper(void)
{
    m_pSideShowService = NULL;
    m_pDeviceHost = NULL;
    m_pContext = NULL;
    m_bInitialized = FALSE;

}

//------------------------------------------------------------------------------
// CDPWSServiceWrapper::~CDPWSServiceWrapper
//      Destructor
//------------------------------------------------------------------------------
CDPWSServiceWrapper::~CDPWSServiceWrapper(void)
{
}

bool g_bSSLdevice = false;

static
bool LoadPreviousWsdUUID(
    __out_ecount(nSize) WCHAR* szUUID, 
    size_t nSize) 
{
    WIN32_FIND_DATA stFindData;
    
    //
    // Load the device ID from any file name matches *.DevID
    //
    HANDLE hSearch;
    
    if (g_bSSLdevice)
    {
        hSearch = FindFirstFile(L"*.SSLWSD", &stFindData);
    }
    else
    {
        hSearch = FindFirstFile(L"*.WSD", &stFindData);
    }

    if (hSearch != INVALID_HANDLE_VALUE)
    {
        FindClose(hSearch);

        WCHAR *pTail = wcsrchr(stFindData.cFileName, L'.');
        if (pTail)
        {
            *pTail = 0;

        HRESULT hr;
            hr = StringCbPrintfW(szUUID, nSize * sizeof(WCHAR), L"%s", stFindData.cFileName);
        if (S_OK == hr)
        return true;
        }
    }

    return false;
}

static void SaveWsdUUID(
        __in WCHAR *psDevID)
{
    // Construct the file name with format *.DevID
    size_t nLen = wcslen(psDevID) + wcslen(L".SSLWSD") + 1;
    
    WCHAR *pFileName = new WCHAR [nLen];

    if (g_bSSLdevice)
    {
        swprintf_s(pFileName, nLen, L"%s.SSLWSD", psDevID);
    }
    else
    {
        swprintf_s(pFileName, nLen, L"%s.WSD", psDevID);
    }

    HANDLE hFile = CreateFile(pFileName, 
                            GENERIC_WRITE, 
                            0, 
                            NULL, 
                            CREATE_NEW, 
                            FILE_ATTRIBUTE_NORMAL, 
                            NULL);

    if (INVALID_HANDLE_VALUE != hFile)
    {
        CloseHandle(hFile);
    }

    delete [] pFileName;
}

static HRESULT GetDeviceAddress(
    __out_ecount(nSize) WCHAR* szDeviceAddress, 
    size_t nSize)
{
    WCHAR szWsdUUID[MAX_PATH] = {0};
    HRESULT hr;

    bool isUUIDLoaded = LoadPreviousWsdUUID(szWsdUUID, sizeof(szWsdUUID) / sizeof(WCHAR));

    if (!isUUIDLoaded)
    {
        UUID    uuid  = {0};
            
        hr = UuidCreate( &uuid );
        if (S_OK != hr)
            return hr;
    
        hr = StringCbPrintfW(
            szWsdUUID, sizeof(szWsdUUID),
            L"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
            uuid.Data1, uuid.Data2, uuid.Data3,
            uuid.Data4[0], uuid.Data4[1], uuid.Data4[2], uuid.Data4[3],
            uuid.Data4[4], uuid.Data4[5], uuid.Data4[6], uuid.Data4[7]);

        if (S_OK != hr)
            return hr;
                
        SaveWsdUUID(szWsdUUID);
    }

    return StringCbPrintfW(
            szDeviceAddress, 
            nSize * sizeof(WCHAR),
            L"urn:uuid:%s", 
            szWsdUUID );
}


//------------------------------------------------------------------------------
// CDPWSServiceWrapper::Initialize
//      Create, initialize Windows SideShow-compatible device and service instances
//------------------------------------------------------------------------------
HRESULT CDPWSServiceWrapper::Initialize(CDPWSSideShowDeviceSimulatorDlg* pWnd)
{
    if (m_bInitialized)
    {
        return S_FALSE;
    }

    HRESULT hr = S_OK;
    WCHAR szDeviceAddress[MAX_PATH] = {0};

    hr = GetDeviceAddress(szDeviceAddress, sizeof(szDeviceAddress) / sizeof(WCHAR));

    if (S_OK != hr)
        return hr;

    // Create the service object that will be plugged into the device host.
    m_pSideShowService = new CSideShowService(pWnd);
    if( NULL == m_pSideShowService )
        return E_OUTOFMEMORY;

    // Plug service to device host
    hr = CreateSideShowServiceTypeHost(
        szDeviceAddress,
        &thisDeviceMetadata,
        m_pSideShowService,
        &m_pDeviceHost,
        &m_pContext
    );

    if( S_OK != hr )
    {
        Cleanup();
        return hr;
    }

    // Start the device host. This sends a HELLO message on the network.
    hr = m_pDeviceHost->Start( 0, NULL, NULL );

    if (S_OK != hr)
    {
        Cleanup();
        return hr;
    }

    // initialize event source utility
    CEventSourceNotificationUtility::GetInstance()->Initialize(m_pDeviceHost);

    m_bInitialized = TRUE;

    return hr;
}

//------------------------------------------------------------------------------
// CDPWSServiceWrapper::StopService
//      Stop, destroy SideShow-compatible device and service instances
//------------------------------------------------------------------------------
HRESULT CDPWSServiceWrapper::StopService()
{
    if (!m_bInitialized)
    {
        return S_FALSE;
    }

    // Stop the device
    if (NULL != m_pDeviceHost)
        m_pDeviceHost->Stop();

    Cleanup();

    // Release event source utility
    CEventSourceNotificationUtility::GetInstance()->DestroyInstance();

    m_bInitialized = FALSE;

    return S_OK;
}

//------------------------------------------------------------------------------
// CDPWSServiceWrapper::Cleanup
//      destroy SideShow-compatible device and service instances
//------------------------------------------------------------------------------
void CDPWSServiceWrapper::Cleanup()
{
    // Stop the device
    if (NULL != m_pDeviceHost)
    {
        m_pDeviceHost->Terminate();
        m_pDeviceHost->Release();
        m_pDeviceHost = NULL;
    }

    if (NULL != m_pContext)
    {
        m_pContext->Release();
        m_pContext = NULL;
    }

    // Release device service
    ReleaseDeviceServices();

    return;
}

//------------------------------------------------------------------------------
// CDPWSServiceWrapper::ReleaseDeviceServices
//      Destroy SideShow-compatible device and service instances
//------------------------------------------------------------------------------
void CDPWSServiceWrapper::ReleaseDeviceServices()
{
    if ( NULL != m_pSideShowService )
    {
        m_pSideShowService->Release();
        m_pSideShowService = NULL;
    }
}